'/
' MOTEP - Massive Online Text Environment Project
' --------------------------------------------------------------------------------
' Shared Code
' 
' ADL.bmx
' 
' Anton Database Language
' 
' Usage:
' 	local database:TTDatabase = CreateADL()
' 	database.LoadADL("C:\Databases\database1.adl")
' 	local integer:int = database.ReadEntryInt("table", "field", "int_entry")
' 	database.WriteEntryInt("table", "field", "int_entry", 123)
' 	database.WriteEntryString("table", "field", "string_entry", "string value")
' 
' Example:
' 	local ServerDatabase:TTDatabase = CreateADL()
' 	ServerDatabase.LoadADL("ADL\server.adl")
' 	local password:string = ServerDatabase.ReadEntryString("accounts", "anthony", "password")
' 
' Note:
' 	This file needs "Strings.bmx", and won't work without it. So include it
' 	either in your main file, or in this file. Also, you need to define the
' 	function "write(str:String)" for it to work. It uses write() to tell you
' 	if something goes wrong. If you don't want to define your own write()
' 	function, just uncomment the one found in "ADL_h.bmx".
' 
' (c) Anton Berlin. All Rights Reserved.
'/


include "..\Shared\ADL_h.bmx"



type TTDatabase

'*** public:

	'// Initialize the database.
	method Initialize:byte()
	
		m_active = false
		m_alive = true
		return 1
	
	end method
	
	'// Terminate the database.
	method Terminate()
	
		ClearList(m_tables)
		m_active = false
		m_alive = false
	
	end method
	
	'// Load a database to memory.
	method LoadADL:byte(url:string)
	
		if (not m_alive) ..
			return 0
		
		local cy:int = 0
		local depth:int = DEPTH_NULL
		
		abortParser = false
		
		'// Load the .adl file into memory, in ReadOnly-Mode.
		local dbstream:TStream = ReadStream(url)
		
		'// Returns 0 if the .adl file was not found.
		if (not dbstream) then
		
			write("...failed to load database ~q" + url + "~q into memory.")
			return 0
		
		end if
		
		'///////////////////////////
		'// The Parser
		'///////////////////////////
		local TTableHolder:TTable = new TTable '// Temporary holder for new TTables.
		local TTFieldHolder:TTField = new TTField '// Temporary holder for new TTFields.
		local TEntryHolder:TEntry = new TEntry '// Temporary holder for new TEntrys.
		
		while (not Eof(dbstream) and not abortParser) '// Read each line of the file.
		
			'// Save the next line to a string.
			local line:string = ReadLine(dbstream)
			
			'// Go through this line, character by character.
			local data:string
			
			for local cx:int = 0 to Len(line)
			
				if (abortParser) ..
					exit
				
				'// Save the current character to a variable.
				local c:string = Mid(line, cx, 1)
				
				'// If this is the beginning of a comment, avoid the rest of this line.
				if (c = "#") ..
					exit
				
				'// Always skip any and all tab characters.
				if (c = "~t") ..
					continue 
				
				'// Save the character to the collected data variable and analyze if what
				'// has been found so far makes sense. If so, reset the data line and continue.
				'// For example, if no table keyword has been found, it will assume you're looking
				'// for a table keyword.
				data = data + c
				
				'// The depth variable tells you what has been found so far. Look for the next
				'// step in the code. For example; if you have found the keyword "TABLE", look for
				'// a quotation-mark, to start build a table name from the code.
				select (depth)
				
					case DEPTH_NULL
						if (str.strip(upper(data)) = "TABLE") then
						
							depth = DEPTH_TABLE
							data = null
						
						end if
					case DEPTH_TABLE
						if (c = "~q") then
						
							depth = DEPTH_TABLE_OPENING_QUOTE
							data = null
						
						else if (c = "[" or c = "]" or c = "\" or c = "{" or c = "}") then
						
							parserError("Expected '~q', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_TABLE_OPENING_QUOTE
						if (c = "~q") then
						
							depth = DEPTH_TABLE_CLOSING_QUOTE
							
							if (data = null) then
							
								parserError("Table name can not be empty.")
								exit
							
							end if
							
							TTableHolder = CreateTable(Mid(data, 0, Len(data)))
							data = null
						
						end if
					case DEPTH_TABLE_CLOSING_QUOTE
						if (c = "[") then
						
							depth = DEPTH_TABLE_OPENING_BRACKET
							data = null
						
						else if (c and c <> " ") then
						
							parserError("Expected '[', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_TABLE_OPENING_BRACKET
						if (str.strip(upper(data)) = "FIELD") then
						
							depth = DEPTH_FIELD
							data = null
						
						else if (c = "]") then
						
							depth = DEPTH_NULL
							TTableHolder = new TTable
							data = null
						
						else if (c = "[" or c = "\" or c = "{" or c = "}" or c = "~q") then
						
							parserError("Expected keyword 'FIELD', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_FIELD
						if (c = "~q") then
						
							depth = DEPTH_FIELD_OPENING_QUOTE
							data = null
						
						else if (c and c <> " ") then
						
							parserError("Expected '~q', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_FIELD_OPENING_QUOTE
						if (c = "~q") then
						
							depth = DEPTH_FIELD_CLOSING_QUOTE
							
							if (data = null) then
							
								parserError("Field name can not be empty.")
								exit
							
							end if
							
							TTFieldHolder = TTableHolder.CreateField(Mid(data, 0, Len(data)))
							data = null
						
						else if (c = "[" or c = "]" or c = "\" or c = "{" or c = "}") then
						
							parserError("Expected string or '~q', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_FIELD_CLOSING_QUOTE
						if (c = "{") then
						
							depth = DEPTH_FIELD_OPENING_BRACKET
							data = null
						
						else if (c = "[" or c = "]" or c = "\" or c = "{" or c = "~q") then
						
							parserError("Expected '{', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_FIELD_OPENING_BRACKET
						if (c = "~q") then
						
							depth = DEPTH_ENTRY_NAME_OPENING_QUOTE
							data = null
						
						else if (c = "}") then
						
							depth = DEPTH_TABLE_OPENING_BRACKET
							TTFieldHolder = new TTField
							data = null
						
						else if (c and c <> " ") then
						
							parserError("Expected '~q', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_ENTRY_NAME_OPENING_QUOTE
						if (c = "~q") then
						
							depth = DEPTH_ENTRY_NAME_CLOSING_QUOTE
							
							if (data = null) then
							
								parserError("Entry name can not be empty.")
								exit
							
							end if
							
							TEntryHolder = TTFieldHolder.CreateEntry(Mid(data, 0, Len(data)))
							data = null
						
						end if
					case DEPTH_ENTRY_NAME_CLOSING_QUOTE
						if (str.strip(lower(data)) = "string") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "string"
							data = null
						
						else if (str.strip(lower(data)) = "bool") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "bool"
							data = null
						
						else if (str.strip(lower(data)) = "short") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "short"
							data = null
						
						else if (str.strip(lower(data)) = "int") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "int"
							data = null
						
						else if (str.strip(lower(data)) = "long") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "long"
							data = null
						
						else if (str.strip(lower(data)) = "float") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "float"
							data = null
						
						else if (str.strip(lower(data)) = "double") then
						
							depth = DEPTH_ENTRY_TYPE
							TEntryHolder.m_type = "double"
							data = null
						
						else if (Len(str.strip(data)) > 1 and c = " ") then
						
							parserError("Expected data type, found '" + data + "'.")
							exit
						
						end if
					case DEPTH_ENTRY_TYPE
						if (c = "=") then
						
							depth = DEPTH_ENTRY_EXPRESSION
							data = null
						
						else if (c and c <> " ") then
						
							parserError("Expected '=', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_ENTRY_EXPRESSION
						if (not c) then
						
							parserError("Expected value Or ';', found NEWLINE.")
							exit
						
						end if
						
						select (lower(TEntryHolder.m_type))
						
							case "string"
								if (c = "~q") then
								
									depth = DEPTH_ENTRY_VALUE_OPENING_QUOTE
									data = null
								
								end if
							case "bool"
								if (c = ";") then
								
									data = str.strip(Mid(data, 0, Len(data)))
									
									if (str.strip(lower(data)) = "true") then
									
										data = "1"
									
									else (if str.strip(lower(data)) = "false") then
									
										data = "0"
									
									end if
									
									if (data <> "1" and data <> "0") then
									
										parserError("Value '" + data + "' is not a boolean.")
										exit
									
									end if
									
									depth = DEPTH_FIELD_OPENING_BRACKET
									TEntryHolder.m_boolvalue = byte(data)
									TEntryHolder = new TEntry
									data = null
								
								end if
							case "short"
								if (c = ";") then
								
									data = str.strip(Mid(data, 0, Len(data)))
									
									try
									
										local i:short = short(data)
									
									catch ex:object
									
										parserError("Value '" + data + "' is not a short.")
										exit
									
									end try
									
									depth = DEPTH_FIELD_OPENING_BRACKET
									TEntryHolder.m_shortvalue = short(data)
									TEntryHolder = new TEntry
									data = null
								
								end if
							case "int"
								if (c = ";") then
								
									data = str.strip(Mid(data, 0, Len(data)))
									
									try
									
										local i:int = int(data)
									
									catch ex:object
									
										parserError("Value '" + data + "' is not a int.")
										exit
									
									end try
									
									depth = DEPTH_FIELD_OPENING_BRACKET
									TEntryHolder.m_intvalue = int(data)
									TEntryHolder = new TEntry
									data = null
								
								end if
							case "long"
								if (c = ";") then
								
									data = str.strip(Mid(data, 0, Len(data)))
									
									try
									
										local i:long = long(data)
									
									catch ex:object
									
										parserError("Value '" + data + "' is not a long.")
										exit
									
									end try
									
									depth = DEPTH_FIELD_OPENING_BRACKET
									TEntryHolder.m_longvalue = long(data)
									TEntryHolder = new TEntry
									data = null
								
								end if
							case "float"
								if (c = ";") then
								
									data = str.strip(Mid(data, 0, Len(data)))
									
									try
									
										local i:float = float(data)
									
									catch ex:object
									
										parserError("Value '" + data + "' is not a float.")
										exit
									
									end try
									
									depth = DEPTH_FIELD_OPENING_BRACKET
									TEntryHolder.m_floatvalue = float(data)
									TEntryHolder = new TEntry
									data = null
								
								end if
							case "double"
								if (c = ";") then
								
									data = str.strip(Mid(data, 0, Len(data)))
									
									try
									
										local i:double = double(data)
									
									catch ex:object
									
										parserError("Value '" + data + "' is not a double.")
										exit
									
									end try
									
									depth = DEPTH_FIELD_OPENING_BRACKET
									TEntryHolder.m_doublevalue = double(data)
									TEntryHolder = new TEntry
									data = null
								
								end if
						
						end select
					case DEPTH_ENTRY_VALUE_OPENING_QUOTE
						if (c = "~q") then
						
							depth = DEPTH_ENTRY_VALUE_CLOSING_QUOTE
							TEntryHolder.m_stringvalue = Mid(data, 0, Len(data))
							data = null
						
						end if
					case DEPTH_ENTRY_VALUE_CLOSING_QUOTE
						if (c = ";") then
						
							depth = DEPTH_FIELD_OPENING_BRACKET
							TEntryHolder = new TEntry
							data = null
						
						else if (c = "\") then
						
							depth = DEPTH_ENTRY_CONTINUING_STRING_VALUE
							data = null
						
						else if (c and c <> " ") then
						
							parserError("Expected ';' or '\', found '" + c + "'.")
							exit
						
						end if
					case DEPTH_ENTRY_CONTINUING_STRING_VALUE
						if (c = "~q") then
						
							depth = DEPTH_ENTRY_CONTINUING_STRING_VALUE_OPENING_QUOTE
							data = null
						
						end if
					case DEPTH_ENTRY_CONTINUING_STRING_VALUE_OPENING_QUOTE
						if (c = "~q") then
						
							depth = DEPTH_ENTRY_VALUE_CLOSING_QUOTE
							TEntryHolder.m_stringvalue :+ Mid(data, 0, Len(data))
							data = null
						
						end if
				
				end select
			
			next
			
			if (depth = DEPTH_TABLE_OPENING_QUOTE) then
			
				parserError("Expected a closing quotation mark, found NEWLINE.")
				exit
			
			end if
		
		end while
		
		if (abortParser) then
		
			write("Failed to load database.")
			return 0
		
		end if
		
		'// Turn the database active, to allow writing to it.
		m_active = true
		
		return 1
	
	end method
	
	'// Save this database to a file.
	method SaveADL(url:string)
		'// Open a stream to url for writing.
		if (FileType(url) = 0) then
		
			local success:int = CreateFile(url)
			if (not success) RuntimeError("Could not create file ~q" + url + "~q.")
		
		end if
		local fileHandle:TStream = WriteStream(url)
		if (not fileHandle) Then
		
			RuntimeError("Could not load ~q" + url + "~q to memory.")
			return false
		
		end if
		
		'// Put in the header.
		WriteLine(fileHandle, "# " + CreateADLHeaderCode())
		WriteLine(fileHandle, "# Created: " + CurrentDate() + " " + CurrentTime())
		
		'// Go through every table, field and entry, and save it to fileHandle.
		for local t:TTable = eachin m_tables
		
			WriteLine(fileHandle, "")
			WriteLine(fileHandle, "TABLE ~q" + t.m_name + "~q [")
			
			for local f:TTField = eachin t.m_fields
			
				WriteLine(fileHandle, "~tFIELD ~q" + f.m_name + "~q {")
				
				for local e:TEntry = eachin f.m_entries
				
					select (e.m_type)
					
						case "string"
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q string = ~q" + e.m_stringvalue + "~q;")
						case "bool"
							local bv:string = e.m_boolvalue
							if (bv = "1") then
							
								bv = "true"
							
							else if (bv = "0") then
							
								bv = "false"
							
							end if
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q bool = " + bv + ";")
						case "short"
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q short = " + e.m_shortvalue + ";")
						case "int"
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q int = " + e.m_intvalue + ";")
						case "long"
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q long = " + e.m_longvalue + ";")
						case "float"
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q float = " + e.m_floatvalue + ";")
						case "double"
							WriteLine(fileHandle, "~t~t~q" + e.m_name + "~q double = " + e.m_doublevalue + ";")
						default
							WriteLine(fileHandle, "~t~t # ~q" + e.m_name + "~q is of an unkown type!")
					
					end select
				
				next
				
				WriteLine(fileHandle, "~t}")
			
			next
			
			WriteLine(fileHandle, "]")
		
		next
		
		'// Close the file stream.
		CloseStream(fileHandle)
	
	end method
	
	'// Write parser error.
	method parserError(str:string)
	
		abortParser = true
		write("[Parser] Syntax error: " + str)
	
	end method
	
	'// 1 = Active, 2 = Inactive - returns 0 if the value is invalid.
	method SetDatabaseState:byte(state:byte)
	
		if (not m_alive) ..
			return 0
		if (state <> 1 and state <> 0) ..
			return 0
		
		m_active = state
		
		return 1
	
	end method
	
	'// Access a table.
	method AccessTable:TTable(dbTable:string)
	
		If (not m_alive) ..
			return null
		If (not m_active) ..
			return null
		
		local tt:TTable = null
		
		for local t:TTable = eachin m_tables
		
			if (t.m_name = dbTable) then
			
				tt = t
				exit
			
			end if
		
		next
		
		return tt
	
	end method
	
	'// Create a table.
	method CreateTable:TTable(tn:string)
	
		local t:TTable = new TTable
		t.m_name = tn
		t.m_owner = self
		ListAddLast(m_tables, t)
		return t
	
	end method
	
	'// Read string from an entry.
	method ReadEntryString:string(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsString()
	
	end method
	
	'// Read bool from an entry.
	method ReadEntryBool:byte(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsBool()
	
	end method
	
	'// Read short from an entry.
	method ReadEntryShort:short(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsShort()
	
	end method
	
	'// Read int from an entry.
	method ReadEntryInt:int(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsInt()
	
	end method
	
	'// Read long from an entry.
	method ReadEntryLong:long(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsLong()
	
	end method
	
	'// Read float from an entry.
	method ReadEntryFloat:float(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsFloat()
	
	end method
	
	'// Read double from an entry.
	method ReadEntryDouble:double(dbTable:string, dbField:string, dbEntry:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).ReadAsDouble()
	
	end method
	
	'// Write string to an entry.
	method WriteEntryString:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:string)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsString(dbValue)
	
	end method
	
	'// Write bool to an entry.
	method WriteEntryBool:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:byte)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsBool(dbValue)
	
	end method
	
	'// Write short to an entry.
	method WriteEntryShort:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:short)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsShort(dbValue)
	
	end method
	
	'// Write int to an entry.
	method WriteEntryInt:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:int)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsInt(dbValue)
	
	end method
	
	'// Write long to an entry.
	method WriteEntryLong:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:long)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsLong(dbValue)
	
	end method
	
	'// Write float to an entry.
	method WriteEntryFloat:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:float)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsFloat(dbValue)
	
	end method
	
	'// Write double to an entry.
	method WriteEntryDouble:byte(dbTable:string, dbField:string, dbEntry:string, dbValue:double)
	
		return AccessTable(dbTable).AccessField(dbField).AccessEntry(dbEntry).WriteAsDouble(dbValue)
	
	end method
	
	field m_active:byte	'// You have to activate a database to be able to read from- / write to- it.
	field m_tables:TList = CreateList()
	field m_alive:byte	'// Once it's not alive, all of it's data is gone, and you can not do anything
					'// with it anymore. You can reinitialize it though, and reuse it that way.

'*** private:

	field newestUID:int = 0
	field abortParser:byte = false

end type

type TTable

	'// Access an entry.
	method AccessField:TTField(dbField:string)
	
		local tf:TTField = null
		
		for local f:TTField = eachin m_fields
		
			if (f.m_name = dbField) then
			
				tf = f
				exit
			
			end if
		
		next
		
		return tf
	
	end method
	
	'// Create a field.
	method CreateField:TTField(fn:string)
	
		local f:TTField = new TTField
		f.m_name = fn
		f.m_owner = self
		ListAddLast(m_fields, f)
		return f
	
	end method
	
	field m_name:string
	field m_fields:TList = CreateList()
	field m_owner:TTDatabase

end type

type TTField

	'// Access a table.
	method AccessEntry:TEntry(dbEntry:string)
	
		local te:TEntry = null
		
		for local e:TEntry = eachin m_entries
		
			if (e.m_name = dbEntry) then
			
				te = e
				exit
			
			end if
		
		next
		
		return te
	
	end method
	
	'// Create an entry.
	method CreateEntry:TEntry(en:string)
	
		local e:TEntry = new TEntry
		e.m_name = en
		e.m_owner = self
		ListAddLast(m_entries, e)
		return e
	
	end method
	
	field m_name:string
	field m_entries:TList = CreateList()
	field m_owner:TTable

end type

type TEntry

	method LockEntry:byte()
	
		if (not m_locked) then
		
			m_locked = true
			return 1
		
		else
		
			return 0
		
		end if
	
	end method
	
	method UnlockEntry:byte()
	
		if (not m_locked) then
		
			m_locked = false
			return 1
		
		else
		
			return 0
		
		end if
	
	end method
	
	method ReadAsString:string()
	
		if (IsString()) ..
			return m_stringvalue ..
		else ..
			return null
	
	end method
	
	method ReadAsBool:byte()
	
		if (IsBool()) ..
			return m_boolvalue ..
		else ..
			return null
	
	end method
	
	method ReadAsShort:short()
	
		if (IsShort()) ..
			return m_shortvalue ..
		else ..
			return null
	
	end method
	
	method ReadAsInt:int()
	
		if (IsInt()) ..
			return m_intvalue ..
		else ..
			return null
	
	end method
	
	method ReadAsLong:long()
	
		if (IsLong()) ..
			return m_longvalue ..
		else ..
			return null
	
	end method
	
	method ReadAsFloat:float()
	
		if (IsFloat()) ..
			return m_floatvalue ..
		else ..
			return null
	
	end method
	
	method ReadAsDouble:double()
	
		if (IsDouble()) ..
			return m_doublevalue ..
		else ..
			return null
	
	end method
	
	method WriteAsString:byte(value:string)
	
		if (m_locked) ..
			return 0
		
		if (IsString()) then
		
			m_stringvalue = value
			return 1
		
		else
			return 0
		end if
	
	end method
	
	method WriteAsBool:byte(value:byte)
	
		if (m_locked) ..
			return 0
		
		if (IsBool()) then
		
			m_boolvalue = value
			return 1
		
		else
			return 0
		end if
	
	end method
	
	method WriteAsShort:byte(value:short)
	
		if (m_locked) ..
			return 0
		
		if (IsShort()) then
		
			m_shortvalue = value
			return 1
		
		else
		
			return 0
		
		end if
	
	end method
	
	method WriteAsInt:byte(value:int)
	
		if (m_locked) ..
			return 0
		
		if (IsInt()) then
		
			m_intvalue = value
			return 1
		
		else
		
			return 0
		
		end if
	
	end method
	
	method WriteAsLong:byte(value:long)
	
		if (m_locked) ..
			return 0
		
		if (IsLong()) then
		
			m_longvalue = value
			return 1
		
		else
		
			return 0
		
		end if
	
	end method
	
	method WriteAsFloat:byte(value:float)
	
		if (m_locked) ..
			return 0
		
		if (IsFloat()) then
		
			m_floatvalue = value
			return 1
		
		else
		
			return 0
		
		end if
	
	End Method
	
	method WriteAsDouble:byte(value:double)
	
		if (m_locked) ..
			return 0
		
		if (IsDouble()) then
		
			m_doublevalue = value
			return 1
		
		else
		
			return 0
		
		end if
	
	end method
	
	method IsLocked:byte(); return m_locked; end method
	
	method IsString:byte()
	
		if (lower(m_type) = "string") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	method IsBool:byte()
	
		if (lower(m_type) = "bool") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	method IsShort:byte()
	
		if (lower(m_type) = "short") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	method IsInt:byte()
	
		if (lower(m_type) = "int") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	method IsLong:byte()
	
		if (lower(m_type) = "long") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	method IsFloat:byte()
	
		if (lower(m_type) = "float") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	method IsDouble:byte()
	
		if (lower(m_type) = "double") ..
			return 1 ..
		else ..
			return 0
	
	end method
	
	field m_name:string
	field m_type:string = "int"
	field m_boolvalue:byte
	field m_shortvalue:short
	field m_intvalue:int
	field m_longvalue:long
	field m_floatvalue:float
	field m_doublevalue:double
	field m_stringvalue:string
	field m_owner:TTField
	
	'// If an entry is locked, you can not write to it.
	field m_locked:byte = false

end type



'// Create a new TDB.
function CreateADL:TTDatabase()

	local d:TTDatabase = new TTDatabase
	d.Initialize()
	return d

end function

'// Debugging tool. Dump everything loaded in a database,
'// to see if it was loaded correctly.
function databaseDump(db:TTDatabase)

	if (db.m_active) then
	
		for local t:TTable = eachin db.m_tables
		
			write("Table: " + t.m_name)
			for local f:TTField = eachin t.m_fields
			
				write("-- Field: " + f.m_name)
				for local e:TEntry = eachin f.m_entries
				
					select (e.m_type)
					
						case "string"
							write("---- Entry: " + e.m_name + " (String) = " + e.m_stringvalue)
						case "bool"
							write("---- Entry: " + e.m_name + " (Boolean) = " + e.m_boolvalue)
						case "short"
							write("---- Entry: " + e.m_name + " (Short) = " + e.m_shortvalue)
						case "int"
							write("---- Entry: " + e.m_name + " (Int) = " + e.m_intvalue)
						case "long"
							write("---- Entry: " + e.m_name + " (Long) = " + e.m_longvalue)
						case "float"
							write("---- Entry: " + e.m_name + " (Float) = " + e.m_floatvalue)
						case "double"
							write("---- Entry: " + e.m_name + " (Double) = " + e.m_doublevalue)
						default
							write("---- Entry: " + e.m_name + " (Unknown) = NULL")
					
					end select
				
				next
			
			next
		
		next
	
	end if

end function

'// Create the header.
function CreateADLHeaderCode:string()

	return "ADLV" + ADL_VERSION + "c" + Mid(str.word(0, ADL_AUTHOR), 1, 1) ..
		 + Mid(str.word(1, ADL_AUTHOR), 1, 1) + Mid(ADL_YEAR, 3, 2)

end function

'// Get tables from a database.
function GetTables:TList(db:TTDatabase); return db.m_tables; end function

'// Get fields from a table.
function GetFields:TList(t:TTable); return t.m_fields; end function

